package parserRecursivo;

import java.util.ArrayList;

import lexer.Tag;
import lexer.Token;
public class Parser {
	
	private Token currentToken;
	private int pos;
	private ArrayList<Token> listaTokens;
	private int linha;
	private boolean temErros = false;

	public Parser(ArrayList<Token> l)
	{
		pos = -1;
		listaTokens = l;
		linha = 1;
		proximoToken();
	}
	
	public void Start()
	{
		parseS();
	}
	
	private void proximoToken()
	{
		pos++;
		if (listaTokens.size() > pos)
		{
			currentToken = listaTokens.get(pos);
			if (currentToken.getTag() == Tag.FIM) linha++;
		}
	}
	
	private void imprimirErro(String m) //throws Exception
	{
		if (!temErros)
		{
			System.err.println("0");
			temErros = true;
		}
		Exception ex = new Exception(linha + ": " + m);
		System.err.println(ex.getMessage());
		System.exit(-1);
		//throw ex;
	}
	
	private void accept (int expectedTag)
	{
		if (currentToken.getTag() == expectedTag)
		{
			proximoToken();
		}
		else
		{
			imprimirErro("esperado: " + Tag.getTag(expectedTag) 
					+ ", recebido: " + Tag.getTag(currentToken.getTag()));
		}
	}
	
	private void parseS()
	{
		parseDECLARACAO();
		parseATRIBUICAO();
		accept(Tag.FIM);
	}
	
	private void parseDECLARACAO()
	{
		if (listaTokens.get(pos + 1).getTag() == Tag.DEFINIRTIPO)
		{
			accept(Tag.VARIAVEL);
			accept(Tag.DEFINIRTIPO);
			parseTIPO();
			accept(Tag.CRIARVARIAVEL);
			parseSD();
		}
	}
	
	private void parseTIPO()
	{
		if (currentToken.getTag() == Tag.STRING
				|| currentToken.getTag() == Tag.FLOAT
				|| currentToken.getTag() == Tag.INTEGER
				|| currentToken.getTag() == Tag.BOOLEAN)
		{
			accept(currentToken.getTag());
		}
		else imprimirErro("esperado: TIPO, recebido: " 
				+ Tag.getTag(currentToken.getTag()));;
	}
	
	private void parseSD()
	{
		if (currentToken.getTag() == Tag.FIM)
		{
			accept(Tag.FIM);
			parseS();
		}
		else if (currentToken.getTag() == Tag.ATRIBUICAO)
		{
			accept(Tag.ATRIBUICAO);
			parseSD2();
			accept(Tag.FIM);
			parseS();
		}
	}
	
	private void parseSD2()
	{
		if (currentToken.getTag() == Tag.VARIAVEL 
				|| currentToken.getTag() == Tag.LITERALNUMERICO 
				|| currentToken.getTag() == Tag.LITERALSTRING
				|| currentToken.getTag() == Tag.TRUE
				|| currentToken.getTag() == Tag.FALSE)
		{
			accept(currentToken.getTag());
		}
		else imprimirErro("esperado: VARIAVEL ou LITERAL, recebido: " 
				+ Tag.getTag(currentToken.getTag()));;
	}
	
	private void parseATRIBUICAO()
	{
		parseAtr();
		parseSA2();
	}
	
	private void parseSA2()
	{
		if (currentToken.getTag() == Tag.SEPARADOR)
		{
			accept(Tag.SEPARADOR);
			parseATRIBUICAO();
		}
	}
	
	private void parseAtr()
	{
		if (currentToken.getTag() != Tag.FIM)
		{
			accept(Tag.VARIAVEL);
			accept(Tag.ATRIBUICAO);
			parseExpr();
		}
	}
	
	private void parseExpr()
	{
		if (currentToken.getTag() == Tag.ABREPARENTESES)
		{
			accept(Tag.ABREPARENTESES);
			parseOp();
			parseExpr2();
			parseOp2();
			accept(Tag.FECHAPARENTESES);
		}
		else
		{
			parseOp();
			parseExpr2();
		}
	}
	
	private void parseExpr2()
	{
		if (currentToken.getTag() == Tag.AND)
		{
			accept(Tag.AND);
			parseExpr();
		}
		else if (currentToken.getTag() == Tag.OR)
		{
			accept(Tag.OR);
			parseExpr();
		}
		else if (currentToken.getTag() == Tag.NOT)
		{
			accept(Tag.NOT);
			parseExpr();
		}
	}
	
	private void parseOp()
	{
		if (currentToken.getTag() == Tag.ABREPARENTESES)
		{
			parseExpr();
		}
		else
		{
			parseSD2();
			parseOp2();
		}
	}
	
	private void parseOp2()
	{
		if (currentToken.getTag() == Tag.SOMA)
		{
			accept(Tag.SOMA);
			parseOp();
		}
		else if (currentToken.getTag() == Tag.SUBTRACAO)
		{
			accept(Tag.SUBTRACAO);
			parseOp();
		}
		else if (currentToken.getTag() == Tag.MULTIPLICACAO)
		{
			accept(Tag.MULTIPLICACAO);
			parseOp();
		}
		else if (currentToken.getTag() == Tag.DIVISAO)
		{
			accept(Tag.DIVISAO);
			parseOp();
		}
		else if (currentToken.getTag() == Tag.MAIOR)
		{
			accept(Tag.MAIOR);
			parseOp();
		}
		else if (currentToken.getTag() == Tag.MENOR)
		{
			accept(Tag.MENOR);
			parseOp();
		}
		else if (currentToken.getTag() == Tag.MAIORIGUAL)
		{
			accept(Tag.MAIORIGUAL);
			parseOp();
		}
		else if (currentToken.getTag() == Tag.MENORIGUAL)
		{
			accept(Tag.MENORIGUAL);
			parseOp();
		}
		else if (currentToken.getTag() == Tag.IGUAL)
		{
			accept(Tag.IGUAL);
			parseOp();
		}
		else if (currentToken.getTag() == Tag.DIFERENTE)
		{
			accept(Tag.DIFERENTE);
			parseOp();
		}
	}
	
	public boolean temErros() {
		return temErros;
	}
}